package com.example.androidfftaudio;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;

public class CSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

	static final long FPS = 10;
	private Context drawContext;
	public DrawThread drawThread;
	private SurfaceHolder drawSurfaceHolder;
	private Boolean threadExists = false;
	private List<Sprite> sprites = new ArrayList<Sprite>();

	// private database db;

	private static final Handler handler = new Handler() {

		public void handleMessage(Message paramMessage) {
		}
	};

	public CSurfaceView(Context ctx, AttributeSet attributeSet) {
		super(ctx, attributeSet);

		drawContext = ctx;
		createSprites();
		init();

	}

	public void init() {

		if (!threadExists) {

			drawSurfaceHolder = getHolder();
			drawSurfaceHolder.addCallback(this);

			// tv = (TextView)(TextView)findViewById(R.id.textView1);

			drawThread = new DrawThread(drawSurfaceHolder, drawContext, handler);

			drawThread.setName("" + System.currentTimeMillis());
			drawThread.start();
		}

		threadExists = Boolean.valueOf(true);

		return;

	}

	public void surfaceChanged(SurfaceHolder paramSurfaceHolder, int paramInt1,
			int paramInt2, int paramInt3) {
		drawThread.setSurfaceSize(paramInt2, paramInt3);
	}

	public void surfaceCreated(SurfaceHolder paramSurfaceHolder) {

		init();

	}

	public void surfaceDestroyed(SurfaceHolder paramSurfaceHolder) {
		int i = 1;
		while (true) {
			if (i == 0)
				return;
			try {
				drawThread.join();
				i = 0;
			} catch (InterruptedException localInterruptedException) {
			}
		}
	}

	private void createSprites() {
		for (int i = 0; i < 21; i++)
			sprites.add(createSprite(R.drawable.icon));
		// sprites.add(createSprite(R.drawable.icon));
	}

	private Sprite createSprite(int resource) {
		Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
		return new Sprite(this, bmp, 1);
	}

	class DrawThread extends Thread {

		private Bitmap soundBackgroundImage;
		private short[] soundBuffer;
		private int[] soundSegmented;
		private double[] soundFFT;
		private double[] soundFFTMag;
		private double[] soundFFTTemp;
		public Boolean restartFlag = Boolean.valueOf(true);
		public Boolean FFTComputed = Boolean.valueOf(false);
		public int FFT_Len = 1024;
		public int STFFT_Len = 256; // short time FFT length
		public int segmentIndex = -1;
		private Paint soundLinePaint;
		private Paint soundLinePaint2;
		private Paint soundLinePaint3;
		private SurfaceHolder soundSurfaceHolder;
		// private Boolean isFirstGS = Boolean.valueOf(false);
		private int firstGS = 0;
		private double[] soundFFTMagTest;
		private int secondGs = 0;
		private int thirdGs = 0;
		private int fourthGs = 0;
		private int fifthGs = 0;
		private int sixthGs = 0;
		private int DO = 0;
		private int RE = 0;
		private int MI = 0;
		private int FA = 0;
		private int SO = 0;
		private int LA = 0;
		private int TI = 0;
		private double max = 0;
		private long startTimeRandom = 0;
		private long eclapsedTimeRandom = 0;
		private long startTime1 = 0;
		private long eclapsedTime1 = 0;
		private long startTime2 = 0;
		private long eclapsedTime2 = 0;
		private long startTimeSpeed = 0;
		private long eclapsedTimeSpeed = 0;
		int min_eclapsedTime = 500;
		int max_eclapsedTime = 700;
		int min_index = 0;
		int max_index[] = { 20, 17 };
		private Boolean[] spriteFlags;
		private int[] spriteX;
		private int[] order;
		private int ordercount = 0;
		private int ordercountCheck = 0;
		private int count = 0;
		private int[] positionXpiano = { 10, 100, 200, 300, 400, 500, 600 };
		private int[] positionXguitar = { 10, 135, 250, 300, 450, 600 };
		private int correct = 0;
		private int wrong = 0;
		private int miss = 0;
		private int speed = 1;
		private int num_sprites[] = { 21, 18 };
		private int num_notes[] = { 7, 6 };
		private int mode = 1; // 0 - piano , 1 - guitar
		private Boolean initFlag = Boolean.valueOf(true);
		// private Boolean[] spriteExist;
		private Boolean isFinished = Boolean.valueOf(true);
		private int previous = -1;
		Random r;
		Random r1;
		int rdn_time, rdn_index;

		public DrawThread(SurfaceHolder paramContext, Context paramHandler,
				Handler arg4) {
			soundSurfaceHolder = paramContext;

			soundLinePaint = new Paint();
			soundLinePaint.setARGB(255, 0, 0, 255);
			soundLinePaint.setStrokeWidth(3);

			soundLinePaint2 = new Paint();
			soundLinePaint2.setAntiAlias(true);
			soundLinePaint2.setARGB(255, 255, 0, 0);
			soundLinePaint2.setStrokeWidth(4);

			soundLinePaint3 = new Paint();
			soundLinePaint3.setAntiAlias(true);
			soundLinePaint3.setARGB(255, 0, 255, 255);
			soundLinePaint3.setStrokeWidth(3);

			soundBuffer = new short[2048];

			soundBackgroundImage = Bitmap.createBitmap(1, 1,
					Bitmap.Config.ARGB_8888);

			soundSegmented = new int[FFT_Len];
			soundFFT = new double[FFT_Len * 2];
			soundFFTMag = new double[FFT_Len];
			soundFFTMagTest = new double[FFT_Len];

			soundFFTTemp = new double[FFT_Len * 2];
			startTimeRandom = System.currentTimeMillis();
			startTime1 = System.currentTimeMillis();
			startTime2 = System.currentTimeMillis();
			startTimeSpeed = System.currentTimeMillis();
			r = new Random();
			r1 = new Random();

			// / procedure = new ArrayList<Integer>();
			// spriteFlags =new ArrayList<Boolean>(2);
			spriteX = new int[num_sprites[mode]];
			spriteFlags = new Boolean[num_sprites[mode]];
			// spriteExist = new Boolean[num_sprites[mode]];
			order = new int[100];
			for (int i = 0; i < num_sprites[mode]; i++) {
				spriteFlags[i] = Boolean.valueOf(false);
				// spriteExist[i] = Boolean.valueOf(false);
			}

		}

		/************* the following performs FFT on a segment of sound ***********/
		public void doDraw(Canvas canvas) {

			// for restart (must init all these variables)
			if (restartFlag && !initFlag) {
				for (int i = 0; i < num_sprites[mode]; i++) {
					spriteFlags[i] = Boolean.valueOf(false);
					sprites.get(i).setY(0);
				}
				ordercount = 0;
				ordercountCheck = 0;
				correct = 0;
				miss = 0;
				count = 0;
				wrong = 0;
				previous = -1;
				speed = 1;
				initFlag = Boolean.valueOf(true);
				// correctFlag = Boolean.valueOf(false);
			}

			// gameplay starts here
			if (restartFlag) {
				eclapsedTimeSpeed = System.currentTimeMillis() - startTimeSpeed;
				if (eclapsedTimeSpeed > 5000) {
					speed++;
					if (speed > 5)
						speed = 6;
					for (int i = 0; i < num_sprites[mode]; i++)
						sprites.get(i).setYspeed(speed);
					startTimeSpeed = System.currentTimeMillis();
				}

				initFlag = Boolean.valueOf(true);
				DO = RE = MI = FA = SO = LA = TI = 0;
				canvas.drawColor(Color.BLACK);
				eclapsedTimeRandom = System.currentTimeMillis()
						- startTimeRandom;
				// randomizing the notes
				if (isFinished) {
					rdn_time = r.nextInt(max_eclapsedTime - min_eclapsedTime
							+ 1)
							+ min_eclapsedTime;
					rdn_index = r1.nextInt(max_index[mode] - min_index + 1)
							+ min_index;
					isFinished = Boolean.valueOf(false);
				}

				if (spriteFlags[rdn_index]
						|| (rdn_index % num_notes[mode]) == (previous % num_notes[mode])) {
					isFinished = Boolean.valueOf(true);
					startTimeRandom = System.currentTimeMillis();
				} else { // set the respective flags and values for the sprite
					if (eclapsedTimeRandom > rdn_time) {
						spriteFlags[rdn_index] = Boolean.valueOf(true);
						order[ordercount] = rdn_index;
						ordercount++;
						spriteX[rdn_index] = positionXpiano[rdn_index
								% num_notes[mode]];
						isFinished = Boolean.valueOf(true);
						startTimeRandom = System.currentTimeMillis();
						if (ordercount >= 100)
							ordercount = 0;
						count++;
						previous = rdn_index;
					}
				}

				eclapsedTime1 = System.currentTimeMillis() - startTime1;
				if (eclapsedTime1 > 200) {
					// start doing sound processing
					if (segmentIndex < 0) {
						segmentIndex = 0;
						while (segmentIndex < FFT_Len) {
							soundSegmented[segmentIndex] = soundBuffer[segmentIndex];
							soundFFT[2 * segmentIndex] = (double) soundSegmented[segmentIndex];
							soundFFT[2 * segmentIndex + 1] = 0.0;
							segmentIndex++;
						}
					}

					int DOcount = 0, REcount = 0, MIcount = 0, FAcount = 0, SOcount = 0, LAcount = 0, TIcount = 0;
					int firstGsCount = 0;
					int secondGsCount = 0;
					int thirdGsCount = 0;
					int fourthGsCount = 0;
					int fifthGsCount = 0;
					int sixthGsCount = 0;
					DoubleFFT_1D fft = new DoubleFFT_1D(FFT_Len);
					fft.complexForward(soundFFT);
					FFTComputed = Boolean.valueOf(true);

					// perform fftshift here
					for (int i = 0; i < FFT_Len; i++) {
						soundFFTTemp[i] = soundFFT[i + FFT_Len];
						soundFFTTemp[i + FFT_Len] = soundFFT[i];
					}
					for (int i = 0; i < FFT_Len * 2; i++) {
						soundFFT[i] = soundFFTTemp[i];
					}

					double mx = -99999;
					for (int i = 0; i < FFT_Len; i++) {
						double re = soundFFT[2 * i];
						double im = soundFFT[2 * i + 1];
						soundFFTMag[i] = Math.sqrt(re * re + im * im);
						soundFFTMagTest[i] = Math.sqrt(re * re + im * im);
						if (soundFFTMag[i] > mx)
							mx = soundFFTMag[i];
						if (soundFFTMag[i] < 1200000) {
							soundFFTMag[i] = 0;
							soundFFTMagTest[i] = 0;
						}
					}
					// normalize the value of the notes
					max = 0;
					double max1 = 0, max2 = 0, max3 = 0;
					for (int i = 0; i < FFT_Len / 2; i++) {
						if (soundFFTMagTest[i] > max3) {
							max = max1;
							max1 = max2;
							max2 = max3;
							max3 = soundFFTMagTest[i];
						} else if (soundFFTMagTest[i] > max2) {
							max = max1;
							max1 = max2;
							max2 = soundFFTMagTest[i];
						} else if (soundFFTMagTest[i] > max1) {
							max = max1;
							max1 = soundFFTMagTest[i];
						} else if (soundFFTMagTest[i] > max)
							max = soundFFTMagTest[i];
					}
					// DOcount = REcount = MIcount = FAcount = SOcount = LAcount
					// = TIcount = 0;
					// checking the piano notes
					if (mode == 0) {
						for (int i = 0; i < FFT_Len / 2; i++) {
							// Do 495,479,445,428,411
							// re 321,379, 437,456,473,474.475,476,
							// mi 406,448, 449, 450, 470,491
							// Fa 354, 445, 467, 468, 489,490
							// So 334, 411, 437 ,436, 462, 487
							// La 427,428, 456, 484
							// Ti 385, 417, 448, 449, 480,481,482
							if (soundFFTMagTest[i] >= max
									&& soundFFTMagTest[i] > 0) {
								if (i == 495 || i == 480 || i == 479
										|| i == 478 || i == 445 || i == 428
										|| i == 411)
									DOcount++;
								if (i == 321 || i == 379 || i == 437
										|| i == 456 || i == 474 || i == 475
										|| i == 476)
									REcount++;
								if (i == 406 || i == 448 || i == 449
										|| i == 450 || i == 470 || i == 491)
									MIcount++;
								if (i == 354 || i == 445 || i == 467
										|| i == 468 || i == 489 || i == 490)
									FAcount++;
								if (i == 334 || i == 411 || i == 437
										|| i == 436 || i == 462 || i == 487
										|| i == 281)
									SOcount++;
								if (i == 399 || i == 427 || i == 428
										|| i == 456 || i == 484 || i == 370
										|| i == 483)
									LAcount++;
								if (i == 385 || i == 417 || i == 448
										|| i == 449 || i == 480 || i == 481
										|| i == 482)
									TIcount++;
							}
						}
						if (DOcount <= 4 && DOcount >= 3)
							DO++;
						if (REcount <= 4 && REcount >= 3)
							RE++;
						if (MIcount <= 4 && MIcount >= 3)
							MI++;
						if (FAcount <= 4 && FAcount >= 3)
							FA++;
						if (SOcount <= 4 && SOcount >= 2)
							SO++;
						if (LAcount <= 4 && LAcount >= 3)
							LA++;
						if (TIcount <= 4 && TIcount >= 3)
							TI++;

						// check whether the first note to be hit is correct or
						// not
						if ((order[ordercountCheck] == 0
								|| order[ordercountCheck] == 7 || order[ordercountCheck] == 14)
								&& DO > 0) {
							DO = 0;
							sprites.get(order[ordercountCheck]).setY(0);
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 1
								|| order[ordercountCheck] == 8 || order[ordercountCheck] == 15)
								&& RE > 0) {
							RE = 0;
							sprites.get(order[ordercountCheck]).setY(0);
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 2
								|| order[ordercountCheck] == 9 || order[ordercountCheck] == 16)
								&& MI > 0) {
							MI = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 3
								|| order[ordercountCheck] == 10 || order[ordercountCheck] == 17)
								&& FA > 0) {
							FA = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 4
								|| order[ordercountCheck] == 11 || order[ordercountCheck] == 18)
								&& SO > 0) {
							SO = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 5
								|| order[ordercountCheck] == 12 || order[ordercountCheck] == 19)
								&& LA > 0) {
							LA = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 6
								|| order[ordercountCheck] == 13 || order[ordercountCheck] == 20)
								&& TI > 0) {
							TI = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if (iswrong()) {
							eclapsedTime2 = System.currentTimeMillis()
									- startTime2;
							startTime2 = System.currentTimeMillis();
							if ((eclapsedTime2 > 300))
								wrong++;
						}
					} else if (mode == 1) {
						// guitar
						for (int i = 0; i < FFT_Len / 2; i++) {
							if (soundFFTMagTest[i] >= max
									&& soundFFTMagTest[i] > 0) {
								if (i == 343 || i == 449 || i == 385
										|| i == 470)
									firstGsCount++;
								if (i == 496 || i == 465 || i == 449
										|| i == 337)
									secondGsCount++;
								if (i == 474 || i == 399 || i == 386
										|| i == 487 || i == 335)
									thirdGsCount++;
								if (i == 493 || i == 484 || i == 437
										|| i == 418)
									fourthGsCount++;
								if (i == 484 || i == 491 || i == 456
										|| i == 406)
									fifthGsCount++;
								if (i == 408 || i == 491 || i == 486
										|| i == 453 || i == 496)
									sixthGsCount++;
							}
						}
						if (firstGsCount <= 4 && firstGsCount >= 2)
							firstGS++;
						if (secondGsCount <= 4 && secondGsCount >= 2)
							secondGs++;
						if (thirdGsCount <= 4 && thirdGsCount >= 2)
							thirdGs++;
						if (fourthGsCount <= 4 && fourthGsCount >= 2)
							fourthGs++;
						if (fifthGsCount <= 4 && fifthGsCount >= 2)
							fifthGs++;
						if (sixthGsCount <= 4 && sixthGsCount >= 2)
							sixthGs++;
						// check whether the first note to be hit is correct or
						// not
						if ((order[ordercountCheck] == 0
								|| order[ordercountCheck] == 6 || order[ordercountCheck] == 12)
								&& firstGS > 0) {
							firstGS = 0;
							sprites.get(order[ordercountCheck]).setY(0);
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 1
								|| order[ordercountCheck] == 7 || order[ordercountCheck] == 13)
								&& secondGs > 0) {
							secondGs = 0;
							sprites.get(order[ordercountCheck]).setY(0);
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 2
								|| order[ordercountCheck] == 8 || order[ordercountCheck] == 14)
								&& thirdGs > 0) {
							thirdGs = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 3
								|| order[ordercountCheck] == 9 || order[ordercountCheck] == 15)
								&& fourthGs > 0) {
							fourthGs = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 4
								|| order[ordercountCheck] == 10 || order[ordercountCheck] == 16)
								&& fifthGs > 0) {
							fifthGs = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if ((order[ordercountCheck] == 5
								|| order[ordercountCheck] == 11 || order[ordercountCheck] == 17)
								&& sixthGs > 0) {
							sixthGs = 0;
							spriteFlags[order[ordercountCheck]] = Boolean
									.valueOf(false);
							sprites.get(order[ordercountCheck]).setY(0);
							ordercountCheck++;
							correct++;
						} else if (firstGS > 0 || secondGs > 0 || thirdGs > 0
								|| fourthGs > 0 || fifthGs > 0 || sixthGs > 0) {
							eclapsedTime2 = System.currentTimeMillis()
									- startTime2;
							startTime2 = System.currentTimeMillis();
							if ((eclapsedTime2 > 300))
								wrong++;
						}
					}
					startTime1 = System.currentTimeMillis();
				}

				// checking if sprites have exceeded the limited position
				for (int i = 0; i < num_sprites[mode]; i++) {
					if (sprites.get(i).isCollision()) {
						sprites.get(i).setY(0);
						spriteFlags[i] = Boolean.valueOf(false);
						ordercountCheck++;
						miss++;
					}
				}
				// displaying the notes
				Sprite temp2 = null;
				for (int i = 0; i < num_sprites[mode]; i++) {
					temp2 = sprites.get(i);
					if (spriteFlags[i]) {
						temp2.onDraw(canvas, spriteX[i], speed);
					}
				}

				if (ordercountCheck >= 100)
					ordercountCheck = 0;

				// user has miss 3 notes,
				if ((miss) >= 3) {
					for (int i = 0; i < num_sprites[mode]; i++) {
						spriteFlags[i] = Boolean.valueOf(false);
					}
					initFlag = Boolean.valueOf(false);
					restartFlag = Boolean.valueOf(false); // is set to false,
															// will become true
															// only when user
															// click "restart"
				}
				segmentIndex = -1;
			}

		}

		private Boolean iswrong() {
			boolean flag = Boolean.valueOf(false);
			if ((!(order[ordercountCheck] == 0)
					&& !(order[ordercountCheck] == 7) || !(order[ordercountCheck] == 14))
					&& DO > 0)
				flag = true;
			if (RE > 0
					&& (!(order[ordercountCheck] == 1)
							&& !(order[ordercountCheck] == 8) && !(order[ordercountCheck] == 15)))
				flag = true;
			if ((!(order[ordercountCheck] == 2)
					&& !(order[ordercountCheck] == 9) && !(order[ordercountCheck] == 16))
					&& MI > 0)
				flag = true;
			if ((!(order[ordercountCheck] == 3)
					&& !(order[ordercountCheck] == 10) && !(order[ordercountCheck] == 17))
					&& FA > 0)
				flag = true;
			if ((!(order[ordercountCheck] == 4)
					&& !(order[ordercountCheck] == 11) && !(order[ordercountCheck] == 18))
					&& SO > 0)
				flag = true;
			if ((!(order[ordercountCheck] == 5)
					&& !(order[ordercountCheck] == 12) && !(order[ordercountCheck] == 19))
					&& LA > 0)
				flag = true;
			if ((!(order[ordercountCheck] == 6)
					&& !(order[ordercountCheck] == 13) && !(order[ordercountCheck] == 20))
					&& TI > 0)
				flag = true;
			return flag;
		}

		public void setBuffer(short[] paramArrayOfShort) {
			synchronized (soundBuffer) {
				soundBuffer = paramArrayOfShort;
				return;
			}
		}

		public void setSurfaceSize(int canvasWidth, int canvasHeight) {
			synchronized (soundSurfaceHolder) {
				soundBackgroundImage = Bitmap.createScaledBitmap(
						soundBackgroundImage, canvasWidth, canvasHeight, true);
				return;
			}
		}

		public void run() {
			while (true) {
				Canvas localCanvas = null;
				try {
					localCanvas = soundSurfaceHolder.lockCanvas(null);
					synchronized (soundSurfaceHolder) {
						if (localCanvas != null)
							doDraw(localCanvas);
					}
				} finally {
					if (localCanvas != null)
						soundSurfaceHolder.unlockCanvasAndPost(localCanvas);
				}
			}

		}

	}

}
